﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Globalization;
using System.Collections;

namespace Xss.Data
{
    public class AdoCommander : IAdoCommander
    {
        public AdoCommander(IAdoSession session)
        {
            Session = session;
        }

        protected virtual System.Data.Common.DbDataAdapter BuildDbAdapter(System.Data.CommandType cmdType, string cmdText, params System.Data.IDbDataParameter[] parameters)
        {
            var cmd = BuildDbCommond(cmdType, cmdText, parameters);

            IDbDataAdapter dataAdapter = Session.DbProvider.CreateDataAdapter();

            //TODO row updated event handling...
            dataAdapter.SelectCommand = cmd;
            dataAdapter.SelectCommand.Connection = Session.Holder.DbConnection;
            //TODO register for warnings on connection.
            dataAdapter.SelectCommand.Transaction = Session.Holder.DbTransaction;
            return dataAdapter as System.Data.Common.DbDataAdapter;
        }

        protected virtual IDbCommand BuildDbCommond(System.Data.CommandType cmdType, string cmdText, params System.Data.IDbDataParameter[] parameters)
        {
            var cmd = Session.Holder.DbConnection.CreateCommand();
            cmd.Transaction = Session.Holder.DbTransaction;
            cmd.CommandTimeout = Session.Source.Timeout;
            cmd.CommandType = cmdType;
            cmd.CommandText = cmdText;
            if (parameters != null && parameters.Length > 0)
                foreach (var p in parameters)
                    cmd.Parameters.Add(p);
            return cmd;
        }

        protected virtual DataTable CreateDataTable(string tableName)
        {
            DataTable dataTable = new DataTable(tableName) { Locale = CultureInfo.InvariantCulture };
            return dataTable;
        }

        protected virtual DataSet CreateDataSet(params DataTable[] datas)
        {
            DataSet ds = new DataSet();
            foreach (var d in datas)
                ds.Tables.Add(d);
            return ds;
        }

        public IAdoSession Session { get; set; }

        public object ExecuteScalar(string cmdText)
        {
            using (var cmd = BuildDbCommond(CommandType.Text, cmdText, null))
                return cmd.ExecuteScalar();
        }

        public object ExecuteScalar(System.Data.CommandType cmdType, string cmdText, params System.Data.IDbDataParameter[] parameters)
        {
            using (var cmd = BuildDbCommond(cmdType, cmdText, parameters))
                return cmd.ExecuteScalar();
        }

        public object ExecuteNonQuery(string cmdText)
        {
            using (var cmd = BuildDbCommond(CommandType.Text, cmdText, null))
                return cmd.ExecuteNonQuery();
        }

        public object ExecuteNonQuery(System.Data.CommandType cmdType, string cmdText, params System.Data.IDbDataParameter[] parameters)
        {
            using (var cmd = BuildDbCommond(cmdType, cmdText, parameters))
                return cmd.ExecuteNonQuery();
        }

        public object ExecuteReader(System.Data.CommandType cmdType, string cmdText, Func<System.Data.IDataReader, object> callback)
        {
            using (var reader = BuildDbCommond(CommandType.Text, cmdText, null).ExecuteReader())
            {
                return callback(reader);
            }
        }

        public object ExecuteReader(System.Data.CommandType cmdType, string cmdText, IReaderCallback callback)
        {
            using (var reader = BuildDbCommond(CommandType.Text, cmdText, null).ExecuteReader())
            {
                return callback.ProcessReader(reader);
            }
        }

        public System.Data.DataTable ExecuteDataTable(string tableName, string cmdText)
        {
            return ExecuteDataTable(CommandType.Text, tableName, cmdText, null);
        }

        public System.Data.DataTable ExecuteDataTable(System.Data.CommandType cmdType, string tableName, string cmdText, params System.Data.IDbDataParameter[] parameters)
        {

            var dataAdapter = BuildDbAdapter(cmdType, cmdText, parameters);
            using (var cmd = dataAdapter.SelectCommand)
            {
                var data = CreateDataTable(tableName);
                dataAdapter.Fill(data);
                return data;
            }

        }

        public System.Data.DataTable ExecuteUpdate(System.Data.DataTable data, string cmdText)
        {
            return ExecuteUpdate(data, CommandType.Text, cmdText, null);
        }

        public System.Data.DataTable ExecuteUpdate(System.Data.DataTable data, System.Data.CommandType cmdType, string cmdText, params System.Data.IDbDataParameter[] parameters)
        {
            var dataAdapter = BuildDbAdapter(cmdType, cmdText, parameters);
            using (var cmd = dataAdapter.SelectCommand)
            {
                var commandBulder = Session.DbProvider.CreateCommandBuilder();
                (commandBulder as System.Data.Common.DbCommandBuilder).DataAdapter = dataAdapter;
                dataAdapter.Update(data);
                return data;
            }
        }

        public System.Data.DataSet ExecuteDataSet(IDictionary cmdText)
        {
            return ExecuteDataSet(cmdText, new DataSet());
        }

        public System.Data.DataSet ExecuteDataSet(System.Collections.IDictionary cmdText, System.Data.DataSet data)
        {
            foreach (DictionaryEntry entry in cmdText)
            {
                data.Tables.Add(ExecuteDataTable(entry.Key.ToString(), entry.Value.ToString()));
            }

            return data;
        }

        public System.Data.DataSet ExecuteUpdate(System.Data.DataSet data, System.Collections.IDictionary cmdText)
        {
            foreach (DictionaryEntry entry in cmdText)
            {
                ExecuteUpdate(data.Tables[entry.Key.ToString()], entry.Value.ToString());
            }

            return data;
        }
    }
}
